<?php
/**
 * Copyright (c) [2019] [selibra]
 * [selibra] is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 * http://license.coscl.org.cn/MulanPSL
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.q
 * See the Mulan PSL v1 for more details.
 */

namespace Selibra\Di;


use application\Product\Service\ProductService;
use Selibra\Aspect\AspectObjectProxy;
use Selibra\Di\AnnotationRegister as SelibraAnnotationRegister;
use Selibra\Di\Container\Container;
use Selibra\Di\Container\ContainerCollector;
use Selibra\Di\Exception\NoImplementClass;
use Selibra\Di\reflection\Reflection;
use Selibra\Tools\Console;
use Selibra\Tools\Tools;
use Swoole\Coroutine;

class DI
{

    /**
     * @var object[]
     */
    private static array $objects = [];


    /**
     * @var Container
     */
    private static Container $container;


    /**
     * @template T
     * @param class-string<T> $id 对象命名空间
     * @param bool $coSingleton 是否使用协程单例(暂不启用)
     * @return T
     */
    public static function getObjectContext(string $id, bool $coSingleton = false): object
    {
        if ($coSingleton && in_array(Coroutine::getCid(), self::$objects)) {
            // 读取单例
            $object = self::$objects[Coroutine::getCid()][$id];
            if (!empty($object)) {
                return $object;
            }
        }
        $container = ContainerCollector::getContainer();
        if ($container->has($id)) {
            $metadata = $container->collector()->get($id);
            if ($metadata->getReflectionClass()->isInterface()) {
                try {
                    $implementationClass = $container->getInterfaceImplementationClass($id);
                } catch (NoImplementClass $exception) {
                    return (new AspectObjectProxy())->getIns($id);
                }
                return self::getObjectContext($implementationClass);
            } else {
                $object = (new AspectObjectProxy())->getIns($id);
            }
        } else {
            // 添加
            $metadata = SelibraAnnotationRegister::getMetadata($id);
            $container->collector()->add($id, $metadata);
            $object = self::getObjectContext($id);
        }
        if ($coSingleton) {
            // 写入单例
            self::$objects[Coroutine::getCid()][$id] = $object;
            Coroutine::defer(function () use ($id) {
                unset(self::$objects[Coroutine::getCid()][$id]);
            });
        }
        return $object;
    }


    /**
     * @param string $annotation
     * @return array<object>
     */
    public static function getAnnotationContexts(string $annotation)
    {
        $objectNamespaces = self::getAnnotationClasses($annotation);
        return array_map(fn($item) => DI::getObjectContext($item), $objectNamespaces);
    }


    /**
     * @param string $annotation
     * @return array<object>
     */
    public static function getAnnotationClasses(string $annotation)
    {
        $container = ContainerCollector::getContainer();
        return $container->collector()->selectObjectByContainerGroup($annotation);
    }


    /**
     * @param Container $container
     */
    public static function setContainer(Container $container)
    {
        self::$container = $container;
    }

    /**
     * @return Container
     */
    public static function getContainer()
    {
        return self::$container;
    }


    /**
     * 获取类的代理Key
     * 该代理Key用于对于代理类的元数据和其他注解的的对象数据做出标识，让能通过代理类找到被代理类的元数据对象
     * @param $class
     * @return string
     */
    public static function getClassProxyClassName($class)
    {
        return 'Proxy' . str_replace('\\', '_', $class);
    }
}
